﻿using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Workflow.Activities;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Design;
using System.Workflow.ComponentModel.Serialization;
using System.Xml;

namespace WindowsWorkflow.Hoster
{
    public partial class WfStateMachineHoster : UserControl, ISite
	{
		#region controls
    	private WorkflowView wfView;
    	private DesignSurface designSurface;
    	private WorkflowLoader wfLoader;
    	private readonly ToolboxService toolboxService;
    	private readonly PropertyGrid propertyGrid;
		#endregion

		#region props
		private string _NamespaceName;
		/// <summary></summary>
		public string NamespaceName
		{
			get
			{
				if (string.IsNullOrEmpty(this._NamespaceName))
					this._NamespaceName = "foo";

				return this._NamespaceName;
			}
			set
			{
				this._NamespaceName = value;
			}
		}

		private string _TypeName;
		/// <summary></summary>
		public string TypeName
		{
			get
			{
				if (string.IsNullOrEmpty(this._TypeName))
					this._TypeName = "Workflow";
				return this._TypeName;
			}
			set
			{
				this._TypeName = value;
			}
		}

		/// <summary></summary>
		public string Xoml
		{
			get
			{
				string xoml = string.Empty;
				if(this.wfLoader !=null)
				{
					this.wfLoader.Flush();
					xoml = this.wfLoader.Xoml;
				}
				return xoml;
			}
			set
			{
				if (!string.IsNullOrEmpty(value))
					this.LoadWorkflow(value);
			}
		}

		/// <summary>
		/// 
		/// </summary>
    	public WorkflowView View
    	{
    		get
    		{
    			return this.wfView;
    		}
    	}
		#endregion

		#region ctor
		public WfStateMachineHoster(PropertyGrid propGrid, Panel toolboxContainer)
        {
            InitializeComponent();
			// theme 
			WorkflowTheme.CurrentTheme.ReadOnly = false;
			WorkflowTheme.CurrentTheme.AmbientTheme.ShowConfigErrors = false;
			WorkflowTheme.CurrentTheme.ReadOnly = true;
			// toolbox
			this.toolboxService=new ToolboxService(this);
			toolboxContainer.Controls.Clear();
			toolboxContainer.Controls.Add(this.toolboxService);
			this.toolboxService.Dock = DockStyle.Fill;
			this.toolboxService.BackColor = this.BackColor;
			this.toolboxService.Font = WorkflowTheme.CurrentTheme.AmbientTheme.Font;
			// property grid
			this.propertyGrid = propGrid;
			this.propertyGrid.Site = this;
			this.propertyGrid.Font = WorkflowTheme.CurrentTheme.AmbientTheme.Font;
			this.propertyGrid.Enter += new EventHandler(propertyGrid_Enter);
		}

		#endregion

		#region load/unload/save
		protected override void OnLoad(EventArgs e)
		{
			base.OnLoad(e);
			this.ShowDefaultWorkflow();
		}

		public void LoadWorkflow(string xoml)
		{
			this.SuspendLayout();
			DesignSurface surface=new DesignSurface();
			WorkflowLoader loader=new WorkflowLoader();
			loader.Xoml = xoml;
			surface.BeginLoad(loader);

			IDesignerHost host = surface.GetService(typeof (IDesignerHost)) as IDesignerHost;
			if(host !=null && host.RootComponent !=null)
			{
				IRootDesigner rootDesigner = host.GetDesigner(host.RootComponent) as IRootDesigner;
				if(rootDesigner !=null)
				{
					this.UnloadWorkflow();

					this.designSurface = surface;
					this.wfLoader = loader;
					this.wfView = rootDesigner.GetView(ViewTechnology.Default) as WorkflowView;
					this.p_Workspace.Controls.Clear();
					this.p_Workspace.Controls.Add(this.wfView);
					this.wfView.Dock = DockStyle.Fill;
					this.wfView.TabIndex = 1;
					this.wfView.TabStop = true;
					this.wfView.HScrollBar.TabStop = false;
					this.wfView.VScrollBar.TabStop = false;
					this.wfView.Focus();
					this.propertyGrid.Site = host.RootComponent.Site;

					ISelectionService selectionService = this.GetService(typeof (ISelectionService)) as ISelectionService;
					if(selectionService !=null)
						selectionService.SelectionChanged += new EventHandler(selectionService_SelectionChanged);
				}
			}

			this.ResumeLayout(true);

			TypeProvider typeProvider = (TypeProvider) this.GetService(typeof (ITypeProvider));
			this.wfLoader.XamlCodeCompileUnit=new CodeCompileUnit();
			this.wfLoader.XamlCodeCompileUnit.Namespaces.Add(
				Helpers.GenerateCodeFromXomlDocument(Helpers.GetRootActivity(xoml),
				this, ref this._NamespaceName, ref this._TypeName));
			typeProvider.AddCodeCompileUnit(this.wfLoader.XamlCodeCompileUnit);
		}

		private void UnloadWorkflow()
		{
			IDesignerHost host = this.GetService(typeof (IDesignerHost)) as IDesignerHost;
			if(host !=null && host.Container.Components.Count>0)
				WorkflowLoader.DestroyObjectGraphFromDesignerHost(host,host.RootComponent as Activity);

			ISelectionService selectionService = this.GetService(typeof (ISelectionService)) as ISelectionService;
			if(selectionService !=null)
				selectionService.SelectionChanged -= new EventHandler(selectionService_SelectionChanged);

			if(this.designSurface !=null)
			{
				this.designSurface.Dispose();
				this.designSurface = null;
			}

			if(this.wfView !=null)
			{
				this.p_Workspace.Controls.Remove(this.wfView);
				this.wfView.Dispose();
				this.wfView = null;
			}
		}

		public void ShowDefaultWorkflow()
		{
			StateMachineWorkflowActivity workflow = new StateMachineWorkflowActivity();
			workflow.Name = "State machine 1";
			string workflowFilePath = Helpers.WorkflowTempFilePath;
			this.TypeName = Path.GetFileNameWithoutExtension(workflowFilePath);
			workflow.SetValue(WorkflowMarkupSerializer.XClassProperty,
			                  string.Format("{0}.{1}", this.NamespaceName, this.TypeName));
			XmlWriter xWriter = XmlTextWriter.Create(workflowFilePath);
			WorkflowMarkupSerializer serializer=new WorkflowMarkupSerializer();
			serializer.Serialize(xWriter,workflow);
			xWriter.Flush();
			xWriter.Close();
			this.LoadWorkflow(workflowFilePath);
		}

		public void Save(string filePath)
		{
			if(this.wfLoader !=null && !string.IsNullOrEmpty(filePath))
			{
				this.wfLoader.Xoml = filePath;
				this.wfLoader.Flush();
				this.TypeName = Path.GetFileNameWithoutExtension(filePath);
			}

			TypeProvider typeProvider = (TypeProvider) this.GetService(typeof (ITypeProvider));
			typeProvider.RemoveCodeCompileUnit(this.wfLoader.XamlCodeCompileUnit);
			this.wfLoader.XamlCodeCompileUnit=new CodeCompileUnit();
			this.wfLoader.XamlCodeCompileUnit.Namespaces.Add(
				Helpers.GenerateCodeFromXomlDocument(
					Helpers.GetRootActivity(string.IsNullOrEmpty(filePath) ? this.wfLoader.Xoml : filePath), this,
					ref this._NamespaceName, ref this._TypeName));
			typeProvider.AddCodeCompileUnit(this.wfLoader.XamlCodeCompileUnit);
		}
		#endregion

		#region selection service 
		void selectionService_SelectionChanged(object sender, EventArgs e)
		{
			ISelectionService selectionService = GetService(typeof(ISelectionService)) as ISelectionService;
			if (selectionService != null)
				this.propertyGrid.SelectedObjects = new ArrayList(selectionService.GetSelectedComponents()).ToArray();
		}
		#endregion

		#region code-gen/compile
		public void CompileWorkflow()
		{
			if(string.IsNullOrEmpty(this.wfLoader.Xoml))
				return;
			if(!File.Exists(this.wfLoader.Xoml))
			{
				MessageBox.Show("Cannot locate xoml file");
				return;
			}

			this.Save(this.wfLoader.Xoml);
			this.Cursor = Cursors.WaitCursor;
			try
			{
				WorkflowCompiler compiler = new WorkflowCompiler();
				WorkflowCompilerParameters parameters = new WorkflowCompilerParameters();
				string codeBesideFilePath = Path.Combine(Path.GetDirectoryName(this.wfLoader.Xoml),
														 Path.GetFileNameWithoutExtension(this.wfLoader.Xoml) + ".cs");
				string ruleFilePath = Path.Combine(Path.GetDirectoryName(this.wfLoader.Xoml),
												   Path.GetFileNameWithoutExtension(this.wfLoader.Xoml) + ".rules");
				ArrayList files = new ArrayList();
				files.Add(codeBesideFilePath);
				if (File.Exists(ruleFilePath))
				{
					string resources = @"resource:" + ruleFilePath + "," + this.NamespaceName + "." + this.TypeName + "." + "rules";
					parameters.CompilerOptions += resources;
				}
				object[] objArray = new object[] { };
				objArray = files.ToArray();
				string[] strArray = new string[objArray.Length];
				Array.Copy(objArray, 0, strArray, 0, objArray.Length);
				parameters.OutputAssembly = Path.Combine(Path.GetDirectoryName(this.wfLoader.Xoml),
														 Path.GetFileNameWithoutExtension(this.wfLoader.Xoml) + ".dll");
				WorkflowCompilerResults results = compiler.Compile(parameters, strArray);
				StringBuilder errors = new StringBuilder();
				foreach (CompilerError compilerError in results.Errors)
				{
					errors.Append(compilerError.ToString() + '\n');
				}
				if (errors.Length != 0)
				{
					MessageBox.Show(this, errors.ToString(), this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
				}
				else
				{
					MessageBox.Show(this, "Workflow compiled successfully. Compiled assembly: \n" + results.CompiledAssembly.GetName(), this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
			finally
			{
				this.Cursor = Cursors.Default;
			}
		}
		#endregion

		#region menu/command service
		public void InvokeStandardCommand(CommandID cmd)
		{
			IMenuCommandService service = this.GetService(typeof (IMenuCommandService)) as IMenuCommandService;
			if (service != null)
				service.GlobalInvoke(cmd);
		}
		#endregion

		#region property grid
		void propertyGrid_Enter(object sender, EventArgs e)
		{
			this.Save(this.wfLoader.Xoml);
		}
		#endregion

		#region Implementation of IServiceProvider

		/// <summary>
        /// Gets the service object of the specified type.
        /// </summary>
        /// <returns>
        /// A service object of type <paramref name="serviceType" />.
        ///  -or- 
        /// null if there is no service object of type <paramref name="serviceType" />.
        /// </returns>
        /// <param name="serviceType">
        /// An object that specifies the type of service object to get. 
        /// </param>
        /// <filterpriority>2</filterpriority>
        public new object GetService(Type serviceType)
        {
        	return (this.wfView != null) ? ((IServiceProvider) this.wfView).GetService(serviceType) : null;
        }
        #endregion

        #region Implementation of ISite

        /// <summary>
        ///                     Gets the component associated with the <see cref="T:System.ComponentModel.ISite" /> when implemented by a class.
        /// </summary>
        /// <returns>
        ///                     The <see cref="T:System.ComponentModel.IComponent" /> instance associated with the <see cref="T:System.ComponentModel.ISite" />.
        /// </returns>
        public IComponent Component
        {
            get { return this; }
        }

        /// <summary>
        ///                     Determines whether the component is in design mode when implemented by a class.
        /// </summary>
        /// <returns>
        /// true if the component is in design mode; otherwise, false.
        /// </returns>
        public new bool DesignMode
        {
            get { return true; }
        }

        #endregion
    }
}
